package com.mapfinal.resource.shapefile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.linuxense.javadbf.DBFDataType;
import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFReader;
import com.linuxense.javadbf.DBFRow;
import com.linuxense.javadbf.DBFWriter;
import com.mapfinal.kit.StringKit;
import com.mapfinal.map.Field;
import com.mapfinal.map.Field.FieldType;
import com.mapfinal.resource.shapefile.dbf.MapField;
import com.mapfinal.resource.shapefile.dbf.MapFields;

public class ShpDBF {

	private String path;
	private String charsetName = "utf-8";
	private DBFReader dbfReader = null;

	public ShpDBF(String path) {
		this.path = path;
	}

	public ShpDBF(String path, String charsetName) {
		this.path = path;
		this.charsetName = StringKit.notBlank(charsetName) ? charsetName : this.charsetName;
	}

	/**
	 * 创建dbf <br>
	 * 创建文件需要使用输出流，这里创建dbf文件时直接指定了字段，也可以不指定字段。
	 * 
	 * @param path:文件路径
	 * @param fieldList：字段
	 * @param charsetName编码字符集
	 * @throws IOException
	 */
	public static ShpDBF create(String path, List<Map<String, String>> fieldList, String charsetName)
			throws IOException {
		DBFField[] fields = new DBFField[fieldList.size()];
		int index = 0;
		for (Map<String, String> fieldMap : fieldList) {
			DBFField field = new DBFField();
			field.setName(fieldMap.get("name"));// 字段名称
			field.setType(DBFDataType.CHARACTER);// 指定字段类型为字符串
			field.setLength(Integer.valueOf(fieldMap.get("length")));// 指定长度
			fields[index] = field;
			index++;
		}
		// 定义DBFWriter实例用来写DBF文件
		DBFWriter dbfWriter = new DBFWriter(new FileOutputStream(path), Charset.forName(charsetName));
		// 设置字段
		dbfWriter.setFields(fields);
		// 写入dbf文件并关闭
		dbfWriter.close();
		return new ShpDBF(path, charsetName);
	}

	/**
	 * 获取字段名 <br>
	 * 利用输入流读取dbf文件，将字段以数组形式输出，需要创建DBFReader对象
	 * 
	 * @param path
	 * @param charsetName
	 * @return
	 * @throws IOException
	 */
	public String[] getFieldName() {
		DBFReader dbfReader = getDbfReader();
		if (dbfReader == null)
			return null;
		int fieldCount = dbfReader.getFieldCount();// 获取字段数量
		String[] fieldName = new String[fieldCount];
		for (int i = 0; i < fieldCount; i++) {
			fieldName[i] = dbfReader.getField(i).getName();
		}
		return fieldName;
	}

	public List<Field> getFields() {
		List<Field> myfields = new ArrayList<Field>();
		DBFReader dbfReader = getDbfReader();
		if (dbfReader == null)
			return myfields;
		int fieldCount = dbfReader.getFieldCount();// 获取字段数量
		for (int i = 0; i < fieldCount; i++) {
			DBFField fld = dbfReader.getField(i);
			String name = fld.getName();
			DBFDataType dataType = fld.getType();
			Field.FieldType ftype = FieldType.CHAR;
			if (dataType != DBFDataType.UNKNOWN) {
				char fieldType = (char) fld.getType().getCode();
				if (fieldType == 'N') {
					// 1表示整形
					ftype = FieldType.INTEGER;
				} else if (fieldType == 'F') {
					// 2表示double
					ftype = FieldType.FLOAT;
				} else if (fieldType == 'C') {
					// 0表示string
					ftype = FieldType.STRING;
				} else if (fieldType == 'L') {
					ftype = FieldType.BOOLEAN;
				} else if (fieldType == 'B') {
					ftype = FieldType.BLOB;
				} else if (fieldType == 'D') {
					ftype = FieldType.DATE;
				} else {
					// 3表示Invaild
					ftype = FieldType.BYTE;
				}
			}
			Field field = new Field(name.trim(), ftype);
			field.setLength(fld.getLength());
			field.setPrecision(fld.getDecimalCount());
			myfields.add(field);
		}
		return myfields;
	}

	/**
	 * 写dbf文件 <br>
	 * 写dbf文件要创建DBFWriter，DBFWriter有参构造方法，有好几个，<br>
	 * 可以传入File对象也可以传入OutputStream，当不指定字符集时将以默认字符集写入（最好指定，不然可能会出现中文乱码）。<br>
	 * 这里是先获取字段，然后根据字段进行排列值，以免出现值错位。<br>
	 * 
	 * @param path:dbf文件路径
	 * @param rowList:要写入的记录行
	 * @param charsetName：字符集
	 * @throws IOException
	 */
	public void write(List<Map<String, String>> rowList) {
		DBFWriter dbfWriter = new DBFWriter(new File(path));
		// 获取字段
		String[] fieldName = getFieldName();
		if (fieldName == null)
			return;
		for (Map<String, String> rowMap : rowList) {
			Object[] rowData = new Object[fieldName.length];
			for (int i = 0; i < rowData.length; i++) {
				// 根据字段来排列指，不然可能出现错位情况
				rowData[i] = rowMap.get(fieldName[i]);
			}
//			rowMap.values().toArray(rowData);
			// 添加记录（此时并没有写入文件）
			dbfWriter.addRecord(rowData);
		}
		// 写入dbf文件并保存关闭
		dbfWriter.close();
	}

	/**
	 * 读dbf记录 <br>
	 * 读取dbf文件将记录以集合的形式返回
	 * 
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public List<Map<String, String>> read() {
		List<Map<String, String>> rowList = new ArrayList<>();
		DBFReader dbfReader = getDbfReader();
		if (dbfReader == null)
			return rowList;
		Object[] rowValues;
		while ((rowValues = dbfReader.nextRecord()) != null) {
			Map<String, String> rowMap = new HashMap<String, String>();
			for (int i = 0; i < rowValues.length; i++) {
				rowMap.put(dbfReader.getField(i).getName(), String.valueOf(rowValues[i]).trim());
			}
			rowList.add(rowMap);
		}
		return rowList;
	}

	public DBFField getField(int i) {
		DBFReader dbfReader = getDbfReader();
		if (dbfReader == null)
			return null;
		// 字段
		DBFField field = dbfReader.getField(i);
		return field;
	}

	public MapFields readRecord(int recordId) {
		MapFields fields = new MapFields();
		DBFReader dbfReader = getDbfReader();
		if (dbfReader == null)
			return fields;
		Object[] rowValues;
		int h = 0;
		while ((rowValues = dbfReader.nextRecord()) != null) {
			if (h == recordId) {
				for (int i = 0; i < rowValues.length; i++) {
					MapField field = new MapField();
					field.setName(dbfReader.getField(i).getName());
					field.setType(dbfReader.getField(i).getType().getCode());
					field.setValue(rowValues[i]);
					fields.add(field);
				}
			}
			h++;
		}
		return fields;
	}

	public DBFRow readRow(int recordId) {
		DBFReader dbfReader = getDbfReader();
		if (dbfReader == null)
			return null;
		DBFRow rowValue;
		int h = 0;
		while ((rowValue = dbfReader.nextRow()) != null) {
			if (h == recordId) {
				break;
			}
			h++;
		}
		return rowValue;
	}

	public void close() {
		if (dbfReader != null) {
			dbfReader.close();
		}
	}

	public String getPath() {
		return path;
	}

	public DBFReader getDbfReader() {
		if (dbfReader == null && StringKit.notBlank(path)) {
			try {
				dbfReader = new DBFReader(new FileInputStream(path), Charset.forName(charsetName));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
		return dbfReader;
	}

}
